<!-- @format -->

<template>
  <div class="comp-code">
    <div class="comp-code__input-box" @click="handleBoxClick">
      <div
        v-for="(item, index) in codeLength"
        :key="index"
        class="comp-code__input-item"
        :class="{
          'is-active': currentIndex === index,
          'is-filled': codeValue[index],
        }"
        @click.stop="handleItemClick(index)"
      >
        <div class="comp-code__input-value">{{ codeValue[index] || '' }}</div>
        <div v-if="currentIndex === index" class="comp-code__cursor"></div>
      </div>
    </div>

    <div v-if="showSendBtn" class="comp-code__send-btn">
      <div v-if="countdown > 0" class="comp-code__countdown">
        {{ countdown }}秒后重新获取
      </div>
      <div
        v-else
        class="comp-code__resend"
        :class="{ 'is-disabled': disabled }"
        @click="handleResend"
      >
        {{ resendText }}
      </div>
    </div>

    <comp-input v-model="inputValue"> </comp-input>

    <!-- 在uniapp中使用原生input组件 -->
    <!-- #ifdef APP-PLUS || H5 -->
    <input
      ref="inputRef"
      v-model="inputValue"
      type="number"
      maxlength="1"
      class="comp-code__hidden-input"
      @input="handleInput"
      @focus="handleFocus"
      @blur="handleBlur"
    />
    <!-- #endif -->

    <!-- #ifdef MP-WEIXIN -->
    <input
      ref="inputRef"
      v-model="inputValue"
      type="number"
      maxlength="1"
      cursor-spacing="100"
      adjust-position
      class="comp-code__hidden-input"
      @input="handleInput"
      @focus="handleFocus"
      @blur="handleBlur"
    />
    <!-- #endif -->
  </div>
</template>

<script setup lang="ts">
  import { ref, computed, watch, onMounted, onBeforeUnmount } from 'vue';

  interface Props {
    /** 验证码长度 */
    length?: number;
    /** 初始值 */
    modelValue?: string;
    /** 是否显示发送按钮 */
    showSendBtn?: boolean;
    /** 倒计时时长(秒) */
    countdown?: number;
    /** 重新发送文本 */
    resendText?: string;
    /** 是否禁用 */
    disabled?: boolean;
    /** 自动聚焦 */
    autoFocus?: boolean;
  }

  const props = withDefaults(defineProps<Props>(), {
    length: 6,
    modelValue: '',
    showSendBtn: true,
    countdown: 60,
    resendText: '获取验证码',
    disabled: false,
    autoFocus: false,
  });

  const emit = defineEmits<{
    /** 更新值 */
    (e: 'update:modelValue', value: string): void;
    /** 输入完成 */
    (e: 'complete', value: string): void;
    /** 重新发送 */
    (e: 'resend'): void;
  }>();

  // 验证码长度
  const codeLength = computed(() => Array.from({ length: props.length }));

  // 输入框引用
  const inputRef = ref<HTMLInputElement | null>(null);

  // 输入值
  const inputValue = ref('');

  // 验证码值数组
  const codeValue = ref<string[]>(Array(props.length).fill(''));

  // 当前输入索引
  const currentIndex = ref(0);

  // 倒计时
  const countdown = ref(0);

  // 倒计时定时器
  let countdownTimer: number | null = null;

  // 监听modelValue变化
  watch(
    () => props.modelValue,
    (newVal) => {
      if (newVal) {
        const valueArray = newVal.split('');
        codeValue.value = Array(props.length).fill('');
        valueArray.forEach((item, index) => {
          if (index < props.length) {
            codeValue.value[index] = item;
          }
        });
        currentIndex.value = Math.min(valueArray.length, props.length);
      } else {
        codeValue.value = Array(props.length).fill('');
        currentIndex.value = 0;
      }
    },
    { immediate: true },
  );

  // 处理输入
  const handleInput = () => {
    if (inputValue.value && currentIndex.value < props.length) {
      // 确保只取输入的第一个字符
      const inputChar = inputValue.value.charAt(0);
      codeValue.value[currentIndex.value] = inputChar;
      currentIndex.value++;
      inputValue.value = '';

      // 更新modelValue
      updateModelValue();

      // 检查是否完成输入
      checkComplete();

      // 如果没有输入完成，保持聚焦状态
      if (currentIndex.value < props.length) {
        // 延迟执行，确保在不同平台上都能正常工作
        setTimeout(() => {
          focus();
        }, 50);
      }
    }
  };

  // 更新modelValue
  const updateModelValue = () => {
    const value = codeValue.value.join('');
    emit('update:modelValue', value);
  };

  // 检查是否完成输入
  const checkComplete = () => {
    const value = codeValue.value.join('');
    if (value.length === props.length) {
      emit('complete', value);
    }
  };

  // 处理输入框容器点击
  const handleBoxClick = () => {
    focus();
  };

  // 处理单个输入框点击
  const handleItemClick = (index: number) => {
    currentIndex.value = index;
    focus();
  };

  // 处理聚焦
  const handleFocus = () => {
    // 找到第一个空值的索引
    const emptyIndex = codeValue.value.findIndex((item) => !item);
    currentIndex.value = emptyIndex >= 0 ? emptyIndex : props.length;
  };

  // 处理失焦
  const handleBlur = () => {
    // 保持当前索引不变
    // 在某些平台上，可能需要延迟重新聚焦以保持输入状态
    // 但这里不做处理，避免在某些场景下造成不必要的聚焦
  };

  // 处理重新发送
  const handleResend = () => {
    if (props.disabled || countdown.value > 0) return;

    emit('resend');
    startCountdown();
  };

  // 开始倒计时
  const startCountdown = () => {
    countdown.value = props.countdown;

    if (countdownTimer) {
      clearInterval(countdownTimer);
    }

    countdownTimer = setInterval(() => {
      if (countdown.value > 0) {
        countdown.value--;
      } else {
        if (countdownTimer) {
          clearInterval(countdownTimer);
          countdownTimer = null;
        }
      }
    }, 1000) as unknown as number;
  };

  // 聚焦输入框
  const focus = () => {
    // 延迟执行，确保在不同平台上都能正常工作
    setTimeout(() => {
      // #ifdef APP-PLUS || H5
      inputRef.value?.focus();
      // #endif

      // #ifdef MP-WEIXIN
      // 在微信小程序中，需要使用不同的方式获取组件实例
      const query = uni.createSelectorQuery();
      query
        .select('.comp-code__hidden-input')
        .fields({ node: true, size: true }, (res: any) => {
          if (res && res.node) {
            res.node.focus();
          } else {
            // 降级处理，尝试直接使用ref
            inputRef.value?.focus();
          }
        })
        .exec();
      // #endif
    }, 100);
  };

  // 清空输入
  const clear = () => {
    codeValue.value = Array(props.length).fill('');
    currentIndex.value = 0;
    inputValue.value = '';
    emit('update:modelValue', '');
  };

  // 设置值
  const setValue = (value: string) => {
    const valueArray = value.split('');
    codeValue.value = Array(props.length).fill('');
    valueArray.forEach((item, index) => {
      if (index < props.length) {
        codeValue.value[index] = item;
      }
    });
    currentIndex.value = Math.min(valueArray.length, props.length);
    updateModelValue();
    checkComplete();
  };

  // 暴露方法
  defineExpose({
    focus,
    clear,
    setValue,
    startCountdown,
  });

  onMounted(() => {
    if (props.autoFocus) {
      // 延迟聚焦，确保组件已完全渲染
      setTimeout(() => {
        focus();
      }, 200);
    }
  });

  onBeforeUnmount(() => {
    if (countdownTimer) {
      clearInterval(countdownTimer);
      countdownTimer = null;
    }
  });
</script>

<style lang="scss" scoped>
  .comp-code {
    width: 100%;

    &__input-box {
      display: flex;
      justify-content: space-between;
      width: 100%;
    }

    &__input-item {
      position: relative;
      width: 50px;
      height: 50px;
      border: 1px solid #dcdfe6;
      border-radius: 4px;
      display: flex;
      justify-content: center;
      align-items: center;
      font-size: 20px;
      font-weight: bold;
      background-color: #fff;
      transition: all 0.2s;

      &.is-active {
        border-color: #409eff;
        box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
      }

      &.is-filled {
        border-color: #dcdfe6;
      }
    }

    &__input-value {
      width: 100%;
      height: 100%;
      display: flex;
      justify-content: center;
      align-items: center;
    }

    &__cursor {
      position: absolute;
      width: 1px;
      height: 20px;
      background-color: #409eff;
      animation: blink 1s infinite;
    }

    &__hidden-input {
      position: absolute;
      top: 0;
      left: 0;
      opacity: 0;
      width: 100%;
      height: 100%;
      z-index: 1;
      pointer-events: none; // 允许点击穿透到下层元素
      caret-color: transparent; // 隐藏光标
      background: transparent;
      color: transparent;
    }

    &__send-btn {
      margin-top: 16px;
      text-align: center;
    }

    &__countdown {
      color: #909399;
      font-size: 14px;
    }

    &__resend {
      color: #409eff;
      font-size: 14px;
      cursor: pointer;

      &.is-disabled {
        color: #c0c4cc;
        cursor: not-allowed;
      }
    }
  }

  @keyframes blink {
    0%,
    100% {
      opacity: 1;
    }
    50% {
      opacity: 0;
    }
  }
</style>
